home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / telecomm / sticpsrc.lzh / SOURCE.ARC / SMTPCLI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-08-10  |  25.1 KB  |  1,099 lines

  1. /*
  2.  *    Client routines for Simple Mail Transfer Protocol ala RFC821
  3.  *    A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *    Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *    Modified 14 June 1987 by P. Karn for symbolic target addresses,
  8.  *    also rebuilt locking mechanism
  9.  *    Limit on max simultaneous sessions, reuse of connections - 12/87 NN2Z
  10.  *    Added return of mail to sender as well as batching of commands 1/88 nn2z
  11.  */
  12.  
  13. #include <stdio.h>
  14. #ifndef ATARI_ST
  15. #include <fcntl.h>
  16. #endif
  17. #include <time.h>
  18. #ifdef UNIX
  19. #include <sys/types.h>
  20. #endif
  21. #include "global.h"
  22. #include "config.h"
  23. #include "netuser.h"
  24. #include "mbuf.h"
  25. #include "timer.h"
  26. #include "tcp.h"
  27. #include "smtp.h"
  28. #include "trace.h"
  29. #include "cmdparse.h"
  30.  
  31. extern int16 lport;            /* local port */
  32. extern int32 resolve();
  33. static struct timer smtpcli_t;        /* client startup timer */
  34. static int32 queuetime = SEC2TICK(500); /* kicktime for queued mail */
  35. int32 gateway;
  36.  
  37. #ifdef SMTPTRACE
  38. int16    smtptrace = 0;            /* used for trace level */
  39. int dosmtptrace();
  40. #endif
  41.  
  42. int16    smtpmaxcli  = MAXSESSIONS;    /* the max client connections allowed */
  43. int16    smtpsessions = 0;        /* number of client connections
  44.                      * currently open */
  45. int16    smtpmode = 0;
  46.  
  47. static struct smtp_cb *cli_session[MAXSESSIONS]; /* queue of client sessions  */
  48.  
  49. static char helocmd[] = "HELO %s\r\nMAIL FROM:<%s>\r\n";
  50. static char mailcmd[] = "MAIL FROM:<%s>\r\n";
  51. static char rcptcmd[] = "RCPT TO:<%s>\r\n";
  52. static char datacmd[] = "DATA\r\n";
  53. static char rsetcmd[] = "RSET\r\n";
  54. static char quitcmd[] = "QUIT\r\n";
  55. static char eom[] = "\r\n.\r\n";
  56.  
  57. static int smtptick(),dogateway(),dosmtpmaxcli(),dotimer();
  58. static int setsmtpmode(),sendwindow();
  59. static void quit(),abort_trans(),sendit(),del_job();
  60. static void execjobs(),smtp_transaction();
  61. static struct smtp_cb *newcb(),*lookup();
  62. static struct smtp_job *setupjob();
  63. void del_session(),logerr(),retmail();
  64. int mlock(),rmlock(),nextjob();
  65. extern void mbxfwd();
  66.  
  67. extern char *getnenv();
  68.  
  69. #if (defined(ATARI_ST) && defined(__TURBOC__))
  70. static void sendit(struct smtp_cb *,char *,...);
  71. #endif
  72.  
  73. struct cmds smtpcmds[] = {
  74.     "gateway",    dogateway,    0,    NULLCHAR,    NULLCHAR,
  75.     "mode",        setsmtpmode,    0,    NULLCHAR,    NULLCHAR,
  76.     "kick",        smtptick,    0,    NULLCHAR,    NULLCHAR,
  77.     "maxclients",    dosmtpmaxcli,    0,    NULLCHAR,    NULLCHAR,
  78.     "timer",    dotimer,    0,    NULLCHAR,    NULLCHAR,
  79. #ifdef SMTPTRACE
  80.     "trace",    dosmtptrace,    0,    NULLCHAR,    NULLCHAR,
  81. #endif
  82.     NULLCHAR,    NULLFP,        0,
  83.     "?subcommands",
  84.         NULLCHAR
  85. };
  86.  
  87. dosmtp(argc,argv)
  88. int argc;
  89. char *argv[];
  90. {
  91.     return subcmd(smtpcmds,argc,argv);
  92. }
  93.  
  94. static int
  95. dosmtpmaxcli(argc,argv)
  96. int argc;
  97. char *argv[];
  98. {
  99.     int x;
  100.     if (argc < 2)
  101.         printf("%d\n",smtpmaxcli);
  102.     else {
  103.         x = atoi(argv[1]);
  104.         if (x > MAXSESSIONS)
  105.             printf("max clients must be <= %d\n",MAXSESSIONS);
  106.         else
  107.             smtpmaxcli = x;
  108.     }
  109.     return 0;
  110. }
  111.  
  112. static int
  113. setsmtpmode(argc,argv)
  114. int argc;
  115. char *argv[];
  116. {
  117.     if (argc < 2) {
  118.         printf("smtp mode: %s\n",
  119.             (smtpmode & QUEUE) ? "queue" : "route");
  120.     } else {
  121.         switch(*argv[1]) {
  122.         case 'q':
  123.             smtpmode |= QUEUE;
  124.             break;
  125.         case 'r':
  126.             smtpmode &= ~QUEUE;
  127.             break;
  128.         default:
  129.             printf("Usage: smtp mode [queue | route]\n");
  130.             break;
  131.         }
  132.     }
  133.     return 0;
  134. }
  135. static int
  136. dogateway(argc,argv)
  137. int argc;
  138. char *argv[];
  139. {
  140.     extern char badhost[];
  141.  
  142.     if(argc < 2){
  143.         printf("%s\n",inet_ntoa(gateway));
  144.     } else if((gateway = resolve(argv[1])) == 0 && argv[1][0] != '['){
  145.         printf(badhost,argv[1]);
  146.         return 1;
  147.     }
  148.     return 0;
  149. }
  150.  
  151. #ifdef SMTPTRACE
  152. static int
  153. dosmtptrace(argc,argv)
  154. int argc;
  155. char *argv[];
  156. {
  157.     if (argc < 2)
  158.         printf("%d\n",smtptrace);
  159.     else
  160.         smtptrace = atoi(argv[1]);
  161.     return 0;
  162. }
  163. #endif
  164.  
  165. /* Set outbound spool poll interval */
  166. static int
  167. dotimer(argc,argv)
  168. int argc;
  169. char *argv[];
  170. {
  171.     if(argc < 2){
  172.         printf("%lu/%lu (%lu)\n",TICK2SEC(smtpcli_t.start - smtpcli_t.count),
  173.                      TICK2SEC(smtpcli_t.start),
  174.                      TICK2SEC(queuetime));
  175.         return 0;
  176.     }
  177.     stop_timer(&smtpcli_t);
  178.     smtpcli_t.func = (void (*)())smtptick;    /* what to call on timeout */
  179.     smtpcli_t.start = SEC2TICK(atol(argv[1])); /* set timer duration */
  180.     start_timer(&smtpcli_t);        /* and fire it up */
  181.     if (argc > 2)                /* queuetime specified? */
  182.         queuetime = SEC2TICK(atol(argv[2]));
  183.     return 0;
  184. }
  185. /* kick the timer when a job is queued */
  186. void
  187. queuekick()
  188. {
  189.     int32 savedstart;
  190.  
  191.     if (queuetime == 0)            /* no queue'd job kick? */
  192.         return;
  193.  
  194.     savedstart = smtpcli_t.start;        /* keep normal smtp timer */
  195.     stop_timer(&smtpcli_t);
  196.     smtpcli_t.func = (void (*)())smtptick;
  197.     smtpcli_t.start = queuetime;        /* set temporary timer value */
  198.     start_timer(&smtpcli_t);        /* and fire it up */
  199.     smtpcli_t.start = savedstart;        /* restore full time value */
  200. }
  201.  
  202. /* this is the routine that gets called every so often to do outgoing mail
  203.    processing */
  204. static int
  205. smtptick()
  206. {
  207.     register struct smtp_cb *cb;
  208.     struct smtp_job *jp;
  209. #ifdef SMTPTRACE
  210.     struct list *ap;
  211. #endif
  212.     char    *mailqdir;
  213.     char    *cp, *cp1;
  214.     int32    destaddr;
  215.     FILE    *wfile;
  216.     char    sysname[LINELEN], tmpstring[LINELEN];
  217.     char    wfilename[13], prefix[9];
  218.     char    mailq[80];
  219.  
  220. #ifdef SMTPTRACE
  221.     if (smtptrace > 5) {
  222.         printf("smtp daemon entered\n");
  223.         fflush(stdout);
  224.     }
  225. #endif
  226.     strcpy(mailq,mailqdir = getnenv(MAILQDIR));
  227.     strcat(mailq,mailqueue);
  228.     for(filedir(mailq,0,wfilename);wfilename[0] != '\0';
  229.         filedir(mailq,1,wfilename)){
  230.  
  231.         /* save the prefix of the file name which it job id */
  232.         cp = wfilename;
  233.         cp1 = prefix;
  234.         while (*cp && *cp != '.')
  235.             *cp1++ = *cp++;
  236.         *cp1 = '\0';
  237.  
  238.         /* lock this file from the smtp daemon */
  239.         if (mlock(mailqdir,prefix))
  240.             continue;
  241.  
  242.         sprintf(tmpstring,"%s%s",mailqdir,wfilename);
  243.         if ((wfile = fopen(tmpstring,"r")) == NULLFILE) {
  244.             /* probably too many open files */
  245.             rmlock(mailqdir,prefix);
  246.             /* continue to next message. The failure
  247.              * may be temporary */
  248.             continue;
  249.         }
  250.  
  251.         fgets(sysname,LINELEN,wfile);    /* read target host */
  252.         rip(sysname);
  253.  
  254.         if ((destaddr = mailroute(sysname)) == 0) {
  255.             fclose(wfile);
  256.             printf("** smtp: Unknown address %s\n",sysname);
  257.             fflush(stdout);
  258.             continue;
  259.         }
  260.  
  261.         if ((cb = lookup(destaddr)) == NULLCB) {
  262.             /* there are enough processes running already */
  263.             if (smtpsessions >= smtpmaxcli) {
  264. #ifdef SMTPTRACE
  265.                 if (smtptrace) {
  266.                     printf("smtp daemon: too many processes\n");
  267.                     fflush(stdout);
  268.                 }
  269. #endif
  270.                 fclose(wfile);
  271.                 rmlock(mailqdir,prefix);
  272.                 continue;
  273.             }
  274.             if ((cb = newcb(tmpstring)) == NULLCB) {
  275.                 fclose(wfile);
  276.                 rmlock(mailqdir,prefix);
  277.                 continue;
  278.             }
  279.             cb->ipdest = destaddr;
  280.         } else {
  281.             /* This system already is sending mail lets not
  282.             * interfere with it's send queue.
  283.             */
  284.             if (cb->state != CLI_INIT_STATE && cb->state != CLI_OPEN_STATE) {
  285.                 fclose(wfile);
  286.                 rmlock(mailqdir,prefix);
  287.                 continue;
  288.             }
  289.         }
  290.  
  291.         fgets(tmpstring,LINELEN,wfile); /* read from */
  292.         rip(tmpstring);
  293.         if ((jp = setupjob(cb,prefix,sysname,tmpstring)) == NULLJOB) {
  294.             fclose(wfile);
  295.             rmlock(mailqdir,prefix);
  296.             del_session(cb);
  297.             continue;
  298.         }
  299.         while (fgets(tmpstring,LINELEN,wfile) != NULLCHAR) {
  300.             rip(tmpstring);
  301.             if (addlist(&jp->to,tmpstring,DOMAIN) == NULLLIST) {
  302.                 fclose(wfile);
  303.                 del_session(cb);
  304.             }
  305.         }
  306.         fclose(wfile);
  307. #ifdef SMTPTRACE
  308.         if (smtptrace > 1) {
  309.             printf("queue job %s From: %s To:",prefix,from);
  310.             for (ap = jp->to; ap != NULLLIST; ap = ap->next)
  311.                 printf(" %s",ap->val);
  312.             printf("\n");
  313.             fflush(stdout);
  314.         }
  315. #endif
  316.     }
  317.  
  318.     /* start sending that mail */
  319.     execjobs();
  320.  
  321.     /* Restart timer */
  322.     start_timer(&smtpcli_t);
  323. }
  324.  
  325. /* this is the master state machine that handles a single SMTP transaction */
  326. static void
  327. smtp_transaction(cb)
  328. register struct smtp_cb *cb;
  329. {
  330.     void smtp_cts();
  331.     register char reply;
  332.     register struct list *tp;
  333.     int cnt;
  334.     struct mbuf *bp,*bpl;
  335.     char tbuf[LINELEN];
  336.     int rcode;
  337.  
  338. #ifdef SMTPTRACE
  339.     if (smtptrace > 5)
  340.         printf("smtp_transaction() enter state=%u\n",cb->state);
  341.     if (smtptrace) {
  342.         printf("%s\n",cb->buf);
  343.         fflush(stdout);
  344.     }
  345. #endif
  346.     /* Another line follows; ignore this one */
  347.     if(cb->buf[0] == '0' || cb->buf[3] == '-')
  348.         return;
  349.  
  350.     /* Got a response - stop the timeout timer */
  351.     stop_timer(&cb->tcb->timeout);
  352.  
  353.     reply = cb->buf[0];
  354.     rcode = atoi(cb->buf);
  355.  
  356.     /* if service shutting down */
  357.     if (rcode == 421) {
  358.         quit(cb);
  359.         return;
  360.     }
  361.  
  362.     switch(cb->state) {
  363.     case CLI_OPEN_STATE:
  364.         if (reply != '2')
  365.             quit(cb);
  366.         else {
  367.             cb->state = CLI_HELO_STATE;
  368.             sendit(cb,helocmd,hostname,cb->jobq->from);
  369.         }
  370.         break;
  371.     case CLI_HELO_STATE:
  372.         if (reply != '2')
  373.             quit(cb);
  374.         else {
  375.             cb->state = CLI_MAIL_STATE;
  376.             start_timer(&cb->tcb->timeout); /* prevent lockup */
  377.         }
  378.         break;
  379.     case CLI_MAIL_STATE:
  380.         if (reply != '2')
  381.             quit(cb);
  382.         else {
  383.             cb->state = CLI_RCPT_STATE;
  384.             cb->rcpts = 0;
  385.             bpl = NULLBUF;
  386.             for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
  387.                 sprintf(tbuf,rcptcmd,tp->val);
  388.                 if ((bp = qstring(tbuf)) == NULLBUF) {
  389.                     free_p(bpl);
  390.                     quit(cb);
  391.                     return;
  392.                 }
  393.                 append(&bpl,bp);
  394.                 cb->rcpts++;
  395. #ifdef SMTPTRACE
  396.                 if (smtptrace) {
  397.                     printf(">>> %s",tbuf);
  398.                     fflush(stdout);
  399.                 }
  400. #endif
  401.             }
  402.             send_tcp(cb->tcb,bpl);
  403.         }
  404.         break;
  405.     case CLI_RCPT_STATE:
  406.         if (reply == '5') {
  407.             logerr(cb);
  408.         } else if (reply == '2') {
  409.             cb->goodrcpt = 1;
  410.         } else {
  411.             /* some kind of temporary failure */
  412.             abort_trans(cb);
  413.             break;
  414.         }
  415.         /* if more rcpts stay in this state (guarded by timer) */
  416.         if (--cb->rcpts != 0) {
  417.             start_timer(&cb->tcb->timeout);
  418.             break;
  419.         }
  420.  
  421.         /* check for no good rcpt on the list */
  422.         if (cb->goodrcpt == 0) {
  423.             if (cb->errlog != NULLLIST)
  424.                 retmail(cb);
  425.             unlink(cb->wname);    /* unlink workfile */
  426.             unlink(cb->tname);    /* unlink text */
  427.             abort_trans(cb);
  428.             break;
  429.         }
  430.         /* if this file open fails abort */
  431.         if ((cb->tfile = fopen(cb->tname,"r")) == NULLFILE)
  432.             abort_trans(cb);
  433.         else {
  434.             /* optimize for slow packet links by sending
  435.              * DATA cmd and the 1st window of text
  436.              */
  437.             bp = qstring(datacmd);
  438.             cb->cts = 1;
  439.             cb->state = CLI_SEND_STATE;
  440.             if ((cnt = cb->tcb->cwind - cb->tcb->sndcnt - 6) > 0) {
  441.                 if (sendwindow(cb,bp,cnt) == EOF)
  442.                        cb->cts = 0;
  443.             } else {
  444.                 send_tcp(cb->tcb,bp);
  445.             }
  446.         }
  447.         break;
  448.     case CLI_SEND_STATE:
  449.         if (reply == '3') {
  450.             cb->state = CLI_UNLK_STATE;
  451.             if(cb->cts == 0)
  452.                 start_timer(&cb->tcb->timeout);
  453.         } else {
  454.             /* change cts to transmit upcall queueing more data */
  455.             cb->cts = 0;
  456.             quit(cb);
  457.         }
  458.         break;
  459.     case CLI_UNLK_STATE:
  460.         /* if a good transfer or permanent failure remove job */
  461.         if (reply == '2' || reply == '5') {
  462.             if (reply == '5')
  463.                 logerr(cb);
  464.             /* close and unlink the textfile */
  465.             if(cb->tfile != NULLFILE) {
  466.                 fclose(cb->tfile);
  467.                 cb->tfile = NULLFILE;
  468.             }
  469.             if (cb->errlog != NULLLIST)
  470.                 retmail(cb);
  471.             unlink(cb->tname);
  472.             unlink(cb->wname);    /* unlink workfile */
  473.             log_tcp(cb->tcb,"SMTP sent job %s To: %s From: %s",
  474.             cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
  475.         }
  476.         if (nextjob(cb)) {
  477.             cb->state = CLI_MAIL_STATE;
  478.             sendit(cb,mailcmd,cb->jobq->from);
  479.         } else {
  480. #ifdef SEND_QUIT_IMMEDIATELY
  481.             /* the quit sent already in smtp_cts */
  482.             cb->state = CLI_QUIT_STATE;
  483.             start_timer(&cb->tcb->timer);
  484. #else
  485.             cb->state = CLI_QUIT_STATE;
  486.             sendit(cb,quitcmd);    /* issue a quit command */
  487. #endif
  488.         }
  489.         break;
  490.     case CLI_IDLE_STATE:    /* used after a RSET and more mail to send */
  491.         if (reply != '2')
  492.             quit(cb);
  493.         else {
  494.             cb->state = CLI_MAIL_STATE;
  495.             sendit(cb,mailcmd,cb->jobq->from);
  496.         }
  497.         break;
  498.     case CLI_QUIT_STATE:
  499.         start_timer(&cb->tcb->timer);
  500.         close_tcp(cb->tcb);        /* close up connection */
  501.         break;
  502.     }
  503. }
  504.  
  505. /* abort the currrent job.
  506.  * If more work exists set up the next job if
  507.  * not then shut down.
  508. */
  509. static void
  510. abort_trans(cb)
  511. register struct smtp_cb *cb;
  512. {
  513.     if(cb->tfile != NULLFILE) {
  514.         fclose(cb->tfile);
  515.         cb->tfile = NULLFILE;
  516.     }
  517.     if (nextjob(cb)) {
  518.         sendit(cb,rsetcmd);
  519.         cb->state = CLI_IDLE_STATE;
  520.         start_timer(&cb->tcb->timeout);
  521.     } else
  522.         quit(cb);
  523. }
  524.  
  525. /* close down link after a failure */
  526. static void
  527. quit(cb)
  528. struct smtp_cb *cb;
  529. {
  530.     cb->state = CLI_QUIT_STATE;
  531.     sendit(cb,quitcmd);        /* issue a quit command */
  532. #ifdef CLOSE_IT_OURSELVES
  533.     close_tcp(cb->tcb);        /* close up connection */
  534. #endif
  535. }
  536.  
  537. /* smtp receiver upcall routine.  fires up the state machine to parse input */
  538. static
  539. void
  540. smtp_rec(tcb,cnt)
  541. struct tcb *tcb;
  542. int16 cnt;
  543. {
  544.     register struct smtp_cb *cb;
  545.     char c;
  546.     struct mbuf *bp;
  547.  
  548. #ifdef SMTPTRACE
  549.     if (smtptrace > 7)  {
  550.         printf("smtp_rec called\n");
  551.         fflush(stdout);
  552.     }
  553. #endif
  554.     cb = (struct smtp_cb *)tcb->user;    /* point to our struct */
  555.     recv_tcp(tcb,&bp,cnt);    /* suck up chars from low level routine */
  556.  
  557.     /* Assemble input line in buffer, return if incomplete */
  558.     while(pullup(&bp,&c,1) == 1) {
  559.         switch(c) {
  560.         case '\r':    /* strip cr's */
  561.             continue;
  562.         case '\n':    /* line is finished, go do it! */
  563.             cb->buf[cb->cnt] = '\0';
  564.             smtp_transaction(cb);
  565.             cb->cnt = 0;
  566.             break;
  567.         default:    /* other chars get added to buffer */
  568.             if(cb->cnt != LINELEN-1)
  569.                 cb->buf[cb->cnt++] = c;
  570.             break;
  571.         }
  572.     }
  573. }
  574.  
  575. /* smtp transmitter ready upcall routine.  twiddles cts flag */
  576. static
  577. void
  578. smtp_cts(tcb,cnt)
  579. struct tcb *tcb;
  580. int16 cnt;
  581. {
  582.     register struct smtp_cb *cb;
  583.  
  584. #ifdef SMTPTRACE
  585.     if (smtptrace > 7) {
  586.         printf("smtp_cts called avail %d\n",cnt);
  587.         fflush(stdout);
  588.     }
  589. #endif
  590.     cb = (struct smtp_cb *)tcb->user;    /* point to our struct */
  591.  
  592.     /* don't do anything until/unless we're supposed to be sending */
  593.     if(cb->cts != 0) {
  594.         if (sendwindow(cb,NULLBUF,cnt) == EOF)
  595.             cb->cts = 0;
  596.     }
  597.  
  598.     if (tcb->sndcnt == 0)            /* no data outstanding? */
  599.         start_timer(&tcb->timeout);    /* watch out for lockups */
  600.     else
  601.         stop_timer(&tcb->timeout);    /* not when data queued */
  602. }
  603.  
  604. /* fill the rest of the window with data and send out the eof commands.
  605. * It is done this way to minimize the number of packets sent.
  606. */
  607. static int
  608. sendwindow(cb,ibp,cnt)
  609. register struct smtp_cb *cb;
  610. struct mbuf *ibp;
  611. int16 cnt;
  612. {
  613.     struct mbuf *bpl = ibp;
  614.     register struct mbuf *bp;
  615.     char *cp;
  616.     int c;
  617.     int last_c = 0;
  618.  
  619.     cnt++;
  620.     while((bp = alloc_mbuf(cnt)) == NULLBUF && (cnt /= 2) > 8)
  621.         ;
  622.     if(bp == NULLBUF){
  623.         /* Hard to know what to do here */
  624.         return EOF;
  625.     }
  626.     cp = bp->data;
  627.     while(cnt > 1 && (c = getc(cb->tfile)) != EOF){
  628. #if (defined(UNIX) || defined(MAC) || defined(AMIGA) || defined(ATARI_ST))
  629.         if(c == '\n'){
  630.             *cp++ = '\r';
  631.             bp->cnt++;
  632.             cnt--;
  633.         }
  634. #endif
  635.         *cp++ = last_c = c;
  636.         bp->cnt++;
  637.         cnt--;
  638.     }
  639.     append(&bpl,bp);
  640.     if (cnt > 1) {    /* EOF seen */
  641.         fclose(cb->tfile);
  642.         cb->tfile = NULLFILE;
  643.         /* send the end of data character. */
  644.         last_c = (last_c == '\n')? 2 : 0;
  645.         if (cnt < sizeof(eom) - 1 - last_c) {
  646.             append(&bpl,qstring(eom + last_c));
  647.             cnt = 0;    /* dont let anyone else in */
  648.         } else {
  649.             memcpy(&bp->data[bp->cnt],eom + last_c,sizeof(eom) - 1 - last_c);
  650.             bp->cnt += sizeof(eom) - 1 - last_c;
  651.             cnt -= sizeof(eom) - 1 - last_c;
  652.         }
  653. #ifdef SEND_QUIT_IMMEDIATELY
  654.         /* send the quit in this packet if last job */
  655.         if (cb->jobq->next == NULLJOB) {
  656.             if (cnt < sizeof(quitcmd) - 1) {
  657.                 bp = qstring(quitcmd);
  658.                 append(&bpl,bp);
  659.             } else {
  660.                 memcpy(&bp->data[bp->cnt],
  661.                 quitcmd,sizeof(quitcmd) - 1);
  662.                 bp->cnt += sizeof(quitcmd) - 1;
  663.             }
  664.         }
  665. #endif
  666.         send_tcp(cb->tcb,bpl);
  667. #ifdef SEND_QUIT_IMMEDIATELY
  668.         if (cb->jobq->next == NULLJOB)
  669.             close_tcp(cb->tcb);    /* close up connection */
  670. #endif
  671.         return EOF;
  672.     } else {
  673.         send_tcp(cb->tcb,bpl);
  674.         return 0;
  675.     }
  676. }
  677.  
  678. /* smtp state change upcall routine. */
  679. static
  680. void
  681. smtp_state(tcb,old,new)
  682. register struct tcb *tcb;
  683. char old,new;
  684. {
  685.     register struct smtp_cb *cb;
  686.     extern char *tcpstates[];
  687.  
  688. #ifdef SMTPTRACE
  689.     if (smtptrace > 7) {
  690.         printf("smtp_state called: %s\n",tcpstates[new]);
  691.         fflush(stdout);
  692.     }
  693. #endif
  694.     cb = (struct smtp_cb *)tcb->user;
  695.     switch(new) {
  696.     case ESTABLISHED:
  697.         cb->state = CLI_OPEN_STATE;    /* shouldn't be needed */
  698.         break;
  699.     case CLOSE_WAIT:
  700.         close_tcp(tcb);            /* shut things down */
  701.         break;
  702.     case CLOSED:
  703.         /* if this close was not done by us ie. a RST */
  704.         if(cb->tfile != NULLFILE)
  705.             fclose(cb->tfile);
  706.         del_session(cb);
  707.         del_tcp(tcb);
  708.         break;
  709.     }
  710. }
  711.  
  712. /* Send message back to server */
  713. /*VARARGS*/
  714. static void
  715. sendit(cb,fmt,arg1,arg2)
  716. struct smtp_cb *cb;
  717. char *fmt,*arg1,*arg2;
  718. {
  719.     char tmpstring[256];
  720.  
  721. #ifdef SMTPTRACE
  722.     if (smtptrace) {
  723.         printf(">>> ");
  724.         printf(fmt,arg1,arg2);
  725.         fflush(stdout);
  726.     }
  727. #endif
  728.     sprintf(tmpstring,fmt,arg1,arg2);
  729.     send_tcp(cb->tcb,qstring(tmpstring));
  730. }
  731.  
  732. /* create mail lockfile */
  733. int
  734. mlock(dir,id)
  735. char *dir,*id;
  736. {
  737.     char lockname[LINELEN];
  738.     int fd;
  739.     /* Try to create the lock file in an atomic operation */
  740.     sprintf(lockname,"%s%s.lck",dir,id);
  741. #ifdef ATARI_ST
  742.     if(!access(lockname,0) || (fd = creat(lockname, 0600)) == -1)
  743. #else
  744.     if((fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT,0600)) == -1)
  745. #endif
  746.         return -1;
  747.     close(fd);
  748.     return 0;
  749. }
  750.  
  751. /* remove mail lockfile */
  752. int
  753. rmlock(dir,id)
  754. char *dir,*id;
  755. {
  756.     char lockname[LINELEN];
  757.     sprintf(lockname,"%s%s.lck",dir,id);
  758.     return(unlink(lockname));
  759. }
  760.  
  761. /* free the message struct and data */
  762. void
  763. del_session(cb)
  764. register struct smtp_cb *cb;
  765. {
  766.     register struct smtp_job *jp,*tp;
  767.     register int i;
  768.  
  769.     if (cb == NULLCB)
  770.         return;
  771.     for(i=0; i<MAXSESSIONS; i++)
  772.         if(cli_session[i] == cb) {
  773.             cli_session[i] = NULLCB;
  774.             break;
  775.         }
  776.  
  777.     if(cb->wname != NULLCHAR)
  778.         free(cb->wname);
  779.     if(cb->tname != NULLCHAR)
  780.         free(cb->tname);
  781.     for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
  782.         tp = jp->next;
  783.         del_job(jp);
  784.     }
  785.     del_list(cb->errlog);
  786.     free((char *)cb);
  787.     smtpsessions--; /* number of connections active */
  788. }
  789.  
  790. static void
  791. del_job(jp)
  792. register struct smtp_job *jp;
  793. {
  794.     if (*jp->jobname != '\0')
  795.         rmlock(getnenv(MAILQDIR),jp->jobname);
  796.     if(jp->system != NULLCHAR)
  797.         free(jp->system);
  798.     if(jp->from != NULLCHAR)
  799.         free(jp->from);
  800.     del_list(jp->to);
  801.     free((char *)jp);
  802. }
  803.  
  804. /* delete a list of list structs */
  805. void
  806. del_list(lp)
  807. struct list *lp;
  808. {
  809.     register struct list *tp, *tp1;
  810.     for (tp = lp; tp != NULLLIST; tp = tp1) {
  811.         tp1 = tp->next;
  812.         if(tp->val != NULLCHAR)
  813.             free(tp->val);
  814.         free((char *)tp);
  815.     }
  816. }
  817.  
  818. /* return message to sender */
  819. void
  820. retmail(cb)
  821. struct smtp_cb *cb;
  822. {
  823.     register struct list *lp;
  824.     register FILE *tfile;
  825.     register int c;
  826.     FILE *infile,*tmpfile();
  827.     char *host,*to;
  828.     time_t t,time();
  829. #ifdef SMTPTRACE
  830.     if (smtptrace > 5) {
  831.         printf("smtp job %s returned to sender\n",cb->wname);
  832.         fflush(stdout);
  833.     }
  834. #endif
  835.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  836.     to = cb->jobq->from;
  837.     if (*to == '\0')
  838.         return;
  839.     if ((host = index(to,'@')) == NULLCHAR)
  840.         host = hostname;
  841.     else
  842.         host++;
  843.     if ((infile = fopen(cb->tname,"r")) == NULLFILE)
  844.         return;
  845.     if ((tfile = tmpfile()) == NULLFILE) {
  846.         fclose(infile);
  847.         return;
  848.     }
  849.     time(&t);
  850.     fprintf(tfile,"Date: %s",ptime(&t));
  851.     fprintf(tfile,"Message-Id: <%ld@%s>\n",get_msgid(),hostname);
  852.     fprintf(tfile,"From: MAILER-DAEMON@%s\n",hostname);
  853.     fprintf(tfile,"To: %s\n",to);
  854.     fprintf(tfile,"Subject: Failed mail\n\n");
  855.     fprintf(tfile,"  ===== Transcript of session with %s follows =====\n\n",cb->jobq->system);
  856.  
  857.     for (lp = cb->errlog; lp != NULLLIST; lp = lp->next)
  858.         fprintf(tfile,"%s\n",lp->val);
  859.  
  860.     fprintf(tfile,"\n  ===== Unsent message follows ====\n");
  861.  
  862.     while((c = getc(infile)) != EOF)
  863.         if (putc(c,tfile) == EOF)
  864.             break;
  865.     fclose(infile);
  866.     fseek(tfile,0L,0);
  867.     if ((smtpmode & QUEUE) != 0)
  868.         router_queue(cb->tcb,tfile,"",to);
  869.     else
  870.         queuejob(cb->tcb,tfile,host,to,"");
  871.     fclose(tfile);
  872. }
  873.  
  874. /* look to see if a smtp control block exists for this ipdest */
  875. static struct smtp_cb *
  876. lookup(destaddr)
  877. int32 destaddr;
  878. {
  879.     register int i;
  880.  
  881.     for(i=0; i<MAXSESSIONS; i++) {
  882.         if(cli_session[i] == NULLCB)
  883.             continue;
  884.         if(cli_session[i]->ipdest == destaddr)
  885.             return cli_session[i];
  886.     }
  887.     return NULLCB;
  888. }
  889.  
  890. /* create a new     smtp control block */
  891. static struct smtp_cb *
  892. newcb()
  893. {
  894.     register int i;
  895.     register struct smtp_cb *cb;
  896.     char *mailqdir;
  897.  
  898.     mailqdir = getnenv(MAILQDIR);
  899.     for(i=0; i<MAXSESSIONS; i++) {
  900.         if(cli_session[i] == NULLCB) {
  901.             cb = (struct smtp_cb *)calloc(1,sizeof(struct smtp_cb));
  902.             if (cb == NULLCB)
  903.                 return(NULLCB);
  904.             cb->wname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
  905.             if (cb->wname == NULLCHAR) {
  906.                 free((char *)cb);
  907.                 return(NULLCB);
  908.             }
  909.             cb->tname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
  910.             if (cb->tname == NULLCHAR) {
  911.                 free(cb->wname);
  912.                 free((char *)cb);
  913.                 return(NULLCB);
  914.             }
  915.             cb->state = CLI_INIT_STATE;
  916.             cli_session[i] = cb;
  917.             smtpsessions++; /* number of connections active */
  918.             return(cb);
  919.         }
  920.     }
  921.     return NULLCB;
  922. }
  923.  
  924. static void
  925. execjobs()
  926. {
  927.     struct socket lsocket, fsocket;
  928.     void smtp_rec(), smtp_cts(), smtp_state();
  929.     register struct smtp_cb *cb;
  930.     register int i;
  931.     char *mailqdir;
  932.  
  933.     mailqdir = getnenv(MAILQDIR);
  934.     for(i=0; i<MAXSESSIONS; i++) {
  935.         cb = cli_session[i];
  936.         if (cb == NULLCB)
  937.             continue;
  938.         if(cb->state != CLI_INIT_STATE)
  939.             continue;
  940.  
  941.         sprintf(cb->tname,"%s%s.txt",mailqdir,cb->jobq->jobname);
  942.         sprintf(cb->wname,"%s%s.wrk",mailqdir,cb->jobq->jobname);
  943. #ifdef AX25
  944.         if (cb->ipdest == 0xffffffffL){ /* check for AX.25 BBS hack */
  945.             cb->state = CLI_MAIL_STATE; /* lock against further adds */
  946.             mbxfwd(cb);        /* forward to BBS(es) */
  947.             continue;
  948.         }
  949. #endif
  950.         /* setup the socket */
  951.         fsocket.address = cb->ipdest;
  952.         fsocket.port = SMTP_PORT;
  953.         lsocket.address = ip_addr;    /* our ip address */
  954.         lsocket.port = lport++;        /* next unused port */
  955. #ifdef SMTPTRACE
  956.         if (smtptrace) {
  957.             printf("Trying Connection to %s\n",inet_ntoa(fsocket.address));
  958.             fflush(stdout);
  959.         }
  960. #endif
  961.  
  962.         /* open smtp connection */
  963.         cb->state = CLI_OPEN_STATE;    /* init state */
  964.         cb->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,tcp_window,
  965.             smtp_rec,smtp_cts,smtp_state,0,(char *)cb);
  966.         cb->tcb->user = (char *)cb;    /* Upward pointer */
  967.     }
  968. }
  969.  
  970. /* add this job to control block queue */
  971. static struct smtp_job *
  972. setupjob(cb,id,systemname,from)
  973. struct smtp_cb *cb;
  974. char *id,*systemname,*from;
  975. {
  976.     register struct smtp_job *p1,*p2;
  977.  
  978.     p1 = (struct smtp_job *)calloc(1,sizeof(struct smtp_job));
  979.     if (p1 == NULLJOB)
  980.         return NULLJOB;
  981.     p1->from = malloc((unsigned)strlen(from) + 1);
  982.     if (p1->from == NULLCHAR) {
  983.         free((char *)p1);
  984.         return NULLJOB;
  985.     }
  986.     p1->system = malloc((unsigned)strlen(systemname) + 1);
  987.     if (p1->system == NULLCHAR) {
  988.         free(p1->from);
  989.         free((char *)p1);
  990.         return(NULLJOB);
  991.     }
  992.     strcpy(p1->system,systemname);
  993.     strcpy(p1->from,from);
  994.     strcpy(p1->jobname,id);
  995.     /* now add to end of jobq */
  996.     if ((p2 = cb->jobq) == NULLJOB)
  997.         cb->jobq = p1;
  998.     else {
  999.         while(p2->next != NULLJOB)
  1000.             p2 = p2->next;
  1001.         p2->next = p1;
  1002.     }
  1003.     return p1;
  1004. }
  1005.  
  1006. /* called to advance to the next job */
  1007. int
  1008. nextjob(cb)
  1009. register struct smtp_cb *cb;
  1010. {
  1011.     register struct smtp_job *jp;
  1012.     char *mailqdir;
  1013.  
  1014.     jp = cb->jobq->next;
  1015.     del_job(cb->jobq);
  1016.     cb->jobq = jp;
  1017.     if (jp == NULLJOB)
  1018.         return 0;
  1019.     /* remove the error log of previous message */
  1020.     del_list(cb->errlog);
  1021.     cb->errlog = NULLLIST;
  1022.     cb->goodrcpt = 0;
  1023.     mailqdir = getnenv(MAILQDIR);
  1024.     sprintf(cb->tname,"%s%s.txt",mailqdir,jp->jobname);
  1025.     sprintf(cb->wname,"%s%s.wrk",mailqdir,jp->jobname);
  1026. #ifdef SMTPTRACE
  1027.     if (smtptrace > 5) {
  1028.         printf("sending job %s\n",jp->jobname);
  1029.         fflush(stdout);
  1030.     }
  1031. #endif
  1032.     return 1;
  1033. }
  1034.  
  1035.  
  1036. /* mail routing function. For now just uses the hosts file */
  1037. int32
  1038. mailroute(dest)
  1039. char *dest;
  1040. {
  1041.     int32 destaddr;
  1042.     char cpdest[80],*cp,*rindex();
  1043.  
  1044.     /* special case: it is destined to us */
  1045.     if (!strcmp(dest,hostname))
  1046.         return ip_addr;            /* return OUR address */
  1047.  
  1048. #ifdef AX25
  1049.     /* if it is of the form call.bbs, check if we can forward */
  1050.     /* to an AX.25 bbs with that name              */
  1051.     if ((cp = index(dest,'.')) != NULLCHAR &&
  1052.         index(cp + 1,'.') == NULLCHAR &&
  1053.         !strncasecmp(cp,".bbs",5)) {
  1054.         sprintf(cpdest,"%s%s",getnenv(MAILQDIR),dest);
  1055.         if (!access(cpdest,4))        /* file exists in MAILQ? */
  1056.         return 0xffffffffL;        /* it is a BBS! */
  1057.     }
  1058. #endif
  1059.     /* look up address or use the gateway */
  1060.     if ((destaddr = resolve(dest)) == 0) {
  1061.         /* if it includes a subdomain (like .ampr.org), remove it.*/
  1062.         /* dangerous, but it allows adresses like pe1chl.ampr.org */
  1063.         /* to be mapped to "pe1chl" in the hosts file (temp fix)  */
  1064.         strcpy(cpdest,dest);
  1065.         while ((cp = rindex(cpdest,'.')) != NULLCHAR){
  1066.         *cp = '\0';
  1067.         if ((destaddr = resolve(cpdest)) != 0)
  1068.             return destaddr;
  1069.         }
  1070.  
  1071.         if (gateway != 0)
  1072.         destaddr = gateway; /* Use the gateway    */
  1073.     }
  1074.     return destaddr;
  1075. }
  1076.  
  1077. /* save error reply for in error list */
  1078. void
  1079. logerr(cb)
  1080. struct smtp_cb *cb;
  1081. {
  1082.     register struct list *lp,*tp;
  1083.     if ((tp = (struct list *)calloc(1,sizeof(struct list))) == NULLLIST)
  1084.         return;
  1085.     if ((tp->val = malloc((unsigned)strlen(cb->buf)+1)) == NULLCHAR) {
  1086.         free((char *)tp);
  1087.         return;
  1088.     }
  1089.     /* find end of list */
  1090.     if ((lp = cb->errlog) == NULLLIST)
  1091.         cb->errlog = tp;
  1092.     else {
  1093.         while(lp->next != NULLLIST)
  1094.             lp = lp->next;
  1095.         lp->next = tp;
  1096.     }
  1097.     strcpy(tp->val,cb->buf);
  1098. }
  1099.